package manager.ota

import event.AckEvent
import event.OtaProgressChanged
import ext.*
import org.greenrobot.eventbus.EventBus
import protocol.BaseProtocol
import serialport.DeliverPortManager
import serialport.Packages
import java.security.MessageDigest
import java.util.concurrent.ArrayBlockingQueue
import java.util.concurrent.TimeUnit


class OtaService(private val frimeware: ByteArray,
                 private val version: Int,
                 private val quitCallback: (Boolean)-> Unit) : Thread() {

    private val mRespQueue = ArrayBlockingQueue<ByteArray>(1)

    private var mPackageIndex = 0
    private var mPackageId = 0

    @Volatile
    private var mCompleteFlag = false

    fun post(bytes: ByteArray) {
        try {
            mRespQueue.add(bytes)
        } catch (e: Exception) {
            e.printStackTrace()
            interrupt()
        }
    }

    private fun onStartResponse(bytes: ByteArray) {
        val state = bytes.arg(1)
        if (state != 0) {
            changeProgress(OtaProgressChanged.STATUS_ERROR)
            rlog("onStartResponse 升级失败:$state")
            throw IllegalStateException("onStartResponse 升级失败:$state")
        }
        nextPackage()
        changeProgress(OtaProgressChanged.STATUS_OK)
    }

    private fun onPackageResponse(bytes: ByteArray) {
        val state = bytes.arg(1)
        if (state != 0) {
            changeProgress(OtaProgressChanged.STATUS_ERROR)
            rlog("onPackageResponse 升级失败:$state")
            throw IllegalStateException("onPackageResponse 升级失败:$state")
        }
        nextPackage()
        changeProgress(OtaProgressChanged.STATUS_OK)
    }

    private fun onCompleteResponse(bytes: ByteArray) {
        val state = bytes.arg(1)
        if (state != 0) {
            changeProgress(OtaProgressChanged.STATUS_ERROR)
            rlog("onCompleteResponse 升级失败:$state")
            throw IllegalStateException("onCompleteResponse 升级失败:$state")
        }
        rlog("升级成功")
        mCompleteFlag = true
        changeProgress(OtaProgressChanged.STATUS_SUCCESS)
    }

    private fun complete() {
        val md5 = MessageDigest.getInstance("MD5").digest( frimeware )
        val buf = BaseProtocol(0x2A).appendAll(md5).build()
        send(buf)
    }

    private fun send(buf: ByteArray) {
        DeliverPortManager.write( Packages(buf, ::onAckEvent) )
    }

    private fun onAckEvent(env: AckEvent?) {
        log("远程升级发送数据:${env}")
    }

    private fun changeProgress(status: Int) {
        EventBus.getDefault().post( OtaProgressChanged(frimeware.size, mPackageIndex, status) )
    }

    private fun nextPackage() {
        val size = if ( frimeware.size - mPackageIndex > 128 ) 128 else {frimeware.size - mPackageIndex}
        if (size <= 0) {
            complete()
            return
        }
        val buf = BaseProtocol(0x29)
            .append7Int2( mPackageId )
            .appendAll( frimeware.subBytes( mPackageIndex, size ) )
            .build()
        mPackageIndex += size
        mPackageId ++
        DeliverPortManager.write( Packages(buf, ::onAckEvent) )
        rlog("发送包:$mPackageId size=$size")
    }

    private fun exec() {
        val bytes = mRespQueue.poll(5, TimeUnit.SECONDS)
        if (bytes == null) {
            changeProgress(OtaProgressChanged.STATUS_ERROR)
            throw IllegalStateException("远程升级超时")
        }
        val action = bytes.action()
        when (action) {
            0xA8 -> onStartResponse(bytes)
            0xA9 -> onPackageResponse(bytes)
            0xAA -> onCompleteResponse(bytes)
            else -> throw IllegalStateException("未知action:$action")
        }
    }

    override fun run() {

        val buf = BaseProtocol(0x28)
            .append7Int4( frimeware.size )
            .append7Int4( version )
            .build()

        send(buf)

        while ( (!interrupted()) && (!mCompleteFlag) ) {
            try {
                exec()
            } catch (e: Exception) {
                e.printStackTrace()
                break
            }
        }
        rlog("远程升级结果:${mCompleteFlag}")
        quitCallback(mCompleteFlag)
        changeProgress(OtaProgressChanged.STATUS_COMPLETE)
    }

}
